package com.cellulargames.control;

import java.util.Hashtable;
import java.util.Random;
import java.util.Vector;

import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;

import com.cellulargames.model.Carta;
import com.cellulargames.model.Grupo;
import com.cellulargames.model.Premio;
import com.cellulargames.util.Util;
import com.cellulargames.view.VideoPoker;

public class Controller {

	public static int MAX_APUESTA = 5;
	
	public static final int MIN_APUESTA = 1;
	
	public static int apuesta;
	
	public static int saldo = 20000;
	
	public static int credito;
	
	public static Carta currentCartas[];
	
	private static Random random;
	
	public static Grupo grupos[];
	
	public static MIDlet midlet;
	
	public static VideoPoker game;
	
	public static final void init(MIDlet mid) throws Exception{
		
		midlet = mid;
		random = new Random();		
		loadPaysList();
		currentCartas = new Carta[5];

		game = new VideoPoker();
		
		Display.getDisplay(midlet).setCurrent(Painter.getInstance());
		
		Painter.getInstance().start();
		
	}
	
	public static void showGame() {
		Painter.getInstance().setCurrentDisplay(game);
		Painter.getInstance().repaint();
	}
	
	/** Aqui en este metodo lo que se hace es que se generan las 5 cartas que se mostrarán en pantalla */
	
	public static final void dealCards() {
		
		random.setSeed(System.currentTimeMillis());
		
		int assign = 0;
		
		while( assign < 5 ){
			
			int val = Math.abs(random.nextInt()%15);
			val = ( val == 0 ) ? 2 : val;
			val = ( val == 1 ) ? 3 : val;
			int figure = Math.abs(random.nextInt()%4);
			
			if( currentCartas[assign] == null ){
				if( !exist(val, figure) ){
					currentCartas[assign] = new Carta(val, figure);
					assign++;
				}
			}
			else{				
				if( !currentCartas[assign].hold ){
					if( !exist(val, figure) ){
						currentCartas[assign].valor = val;
						currentCartas[assign].figura =  figure;
						assign++;
					}
				}
				else{
					assign++;
				}
			}
			
		}
		
	}
	
	private static boolean exist(int val, int fig) {
		for(int i=0;i<5;i++){
			if( currentCartas[i] != null && currentCartas[i].valor == val && currentCartas[i].figura == fig ){
				return true;
			}
		}
		return false;
	}
	
/*
											Tabla de Pagos
					
								1 Ficha	  2 Fichas 	 3 Fichas 	 4 Fichas 	5 Fichas
			Escalera Real 		250 	   500 		   750 		  1000 		Premio Acumulado
			Escala de Color 	50 		   100 		   150 		  200 		250
			Cuatro Iguales 		25 		   50 		   75 		  100 		125
			Full 				8 		   16 		   24 		  32 		40
			Color 				5 		   10 		   15 		  20 		25
			Escalera 			4 			8 		   12 		  16 		20
			Trío 				3 		    6 			9 		  12 		15
			Doble Pareja 		2 		    4 		    6 		   8 		10
			Jotas o Mejor 		1 			2 			3 		   4 		 5 

*/
	
	private static Hashtable premios;
	
	private static final void loadPaysList(){
		premios = new Hashtable();
		premios.put(new Integer(Premio.ESCALERA_REAL), new Integer(250));
		premios.put(new Integer(Premio.ESCALA_DE_COLOR), new Integer(50));
		premios.put(new Integer(Premio.CUATRO_IGUALES), new Integer(25));
		premios.put(new Integer(Premio.FULL), new Integer(8));
		premios.put(new Integer(Premio.COLOR), new Integer(5));
		premios.put(new Integer(Premio.ESCALERA), new Integer(4));
		premios.put(new Integer(Premio.TRIO), new Integer(3));
		premios.put(new Integer(Premio.DOBLE_PAREJAS), new Integer(2));
		premios.put(new Integer(Premio.JOTAS_O_MEJOR), new Integer(1));
	}
	
	public static final void holdCard(int cardPos) {
		currentCartas[cardPos].hold = !currentCartas[cardPos].hold;
	}
	/**
	 * @return Una cadena que representa la mano actual.
	 */
	public static final String repCartas(){
		String rep = "";
		for(int i = 0; i < 5; i++){
			rep += currentCartas[i].toString()+"\n";
		}
		return rep;
	}
	
	/**
	 * 
	 * Este metodo permite setear el currentCartas
	 * Esto para probar los metodos de chequeo y no
	 * tener que depender de la suerte-
	 */
	public static final void setCurrent(Carta[] cartas){
		//Solo agrega 5.
		for(int i = 0; i < 5; i++){
			currentCartas[i] = cartas[i];
		}
	}
	
	public static final void reset() {
		currentCartas[0].hold = false;
		currentCartas[1].hold = false;
		currentCartas[2].hold = false;
		currentCartas[3].hold = false;
		currentCartas[4].hold = false;
	}
	
	public static void print(){
		for(int i=0;i<5;i++){
			System.out.println( currentCartas[i] );
		}
	}
	
	/**
	 * 
	 * Para chequear :D
	 
	public static final void checks(){
		System.out.println("Color?: "+checkColor());
		System.out.println("Escalera?: "+checkEscalera());
		System.out.println("Escalera Color?: "+checkEscaleraColor());
		System.out.println("Escalera Real?: "+checkEscaleraReal());
		System.out.println("Jacks o mejor?: "+checkJotas(getGrupos()));
		System.out.println("Doble Par?: "+checkDobleP(getGrupos()));
		System.out.println("Trio?: "+checkTrio(getGrupos()));
		System.out.println("Full House?: "+checkFull(getGrupos()));
		System.out.println("Quads?: "+checkCuatro(getGrupos()));		
	}
	
	/**
	 * Grupos:
	 * - Repeticiones: Jotas o mejor (Jacks or Better), Doble pareja (Two Pair), Trio (Three of a Kind), Full (Full House), Cuatro iguales (Four of a kind)
	 * - Escaleras: Escalera (Straight), Escala de color (Straight Flush), Escalera real (Royal Flush)
	 * - Otros: Color (Flush)
	 * */
	
	public static final int getPay() {
		
		if( checkEscaleraReal() ){
			System.out.println("Escalera real");
			return ((Integer)premios.get(new Integer(Premio.ESCALERA_REAL))).intValue()*apuesta;
		}
		else if( checkEscaleraColor() ){
			System.out.println("Escalera Color");
			return ((Integer)premios.get(new Integer(Premio.ESCALA_DE_COLOR))).intValue()*apuesta;		
		}
		else if( checkColor() ){
			System.out.println("Color");
			return ((Integer)premios.get(new Integer(Premio.COLOR))).intValue()*apuesta;
		}
		else if( checkEscalera() ){
			System.out.println("Escalera");
			return ((Integer)premios.get(new Integer(Premio.ESCALERA))).intValue()*apuesta;
		}
		
		Grupo group[] = getGrupos();
		
		if( checkJotas(group) ){
			System.out.println("Jotas");
			return ((Integer)premios.get(new Integer(Premio.JOTAS_O_MEJOR))).intValue()*apuesta;
		}
		else if( checkDobleP(group) ){
			System.out.println("Doble P");
			return ((Integer)premios.get(new Integer(Premio.DOBLE_PAREJAS))).intValue()*apuesta;
		}
		else if( checkTrio(group) ){
			System.out.println("Trio");
			return ((Integer)premios.get(new Integer(Premio.TRIO))).intValue()*apuesta;
		}
		else if( checkFull(group) ){
			System.out.println("Full");
			return ((Integer)premios.get(new Integer(Premio.FULL))).intValue()*apuesta;
		}
		else if( checkCuatro(group) ){
			System.out.println("Cuatro");
			return ((Integer)premios.get(new Integer(Premio.CUATRO_IGUALES))).intValue()*apuesta;
		}
		
		return 0;
		
	}
	
	/**
	 * A flush is a poker hand which contains five cards of the same suit, not in rank sequence
	 * 
	 * @return Regresa true si se tiene color (Flush)
	 * 
	 * TODO: No creo que este (grupo de) metodo(s) se use por fuera de esta clase. pero si se necesita. seria hacerlo public
	 */
	static final boolean checkColor(){
		// Uso uno aparte para hacer mas facil la lectura del return demonico de abajo
		int figura0 = currentCartas[0].figura; 
		return((currentCartas[1].figura == figura0) && 
			   (currentCartas[2].figura == figura0) && 
			   (currentCartas[3].figura == figura0) && 
			   (currentCartas[4].figura == figura0));
	}
	
	/**
	 * 
	 * 
	 * A straight is a poker hand which contains five cards of sequential rank but in more than one suit
	 * @return Regresa true si se tiene Escalera (Straight)
	 */
	static final boolean checkEscalera(){
		//"Organizar" el vector
		Carta currCartas[] = {currentCartas[0], currentCartas[1], currentCartas[2], currentCartas[3], currentCartas[4]};
		Util.quicksort(currCartas);
		int sigma_delta = 0;
		int delta = 0;
		for(int i = 0; i < 4 ; i++){
			delta = Math.abs(currCartas[i].valor - currCartas[i+1].valor);
			//System.out.println("Delta: "+delta);
			sigma_delta += delta;
			//System.out.println("Sigma Delta: "+sigma_delta);
		}
		
		if(sigma_delta == 4){
			return true;
		}else{
			/*caso especial. A como 1
			* Aces can play low in straights and straight flushes: 5 4 3 2 A is a 5-high straight flush, 
			* also known as a "steel wheel"
			* 
			* Bastante unica combinacion. creo yo :D
			* Si no llega a ser escalera con As como 1 o escalera normal, no es ninguna escalera
			*/
			return ((currCartas[0].valor == 2) && 
					(currCartas[1].valor == 3) &&
					(currCartas[2].valor == 4) &&
					(currCartas[3].valor == 5) &&
					(currCartas[4].valor == Carta.A)); 

		}
	}
	
	/**
	 * A straight flush is a hand that contains five cards in sequence, all of the same suit
	 * 
	 * @return Regresa true si se tiene Escalera de color (Straight Flush)
	 */
	static final boolean checkEscaleraColor(){
		return (checkEscalera() && checkColor());
	}
	
	/**
	 * An ace-high straight flush such as A K Q J 10 is known as a royal flush
	 * Es un caso especial de la Escalera de Color.
	 * @return Regresa true si se tiene Escalera Real (Royal Flush)
	 */
	static final boolean checkEscaleraReal(){
		return ( checkColor() && 
				(currentCartas[0].valor == 10) && 
				(currentCartas[1].valor == Carta.J) &&
				(currentCartas[2].valor == Carta.Q) &&
				(currentCartas[3].valor == Carta.K) &&
				(currentCartas[4].valor == Carta.A)); 	
	}
	/**
	 * 
	 * 
	 * One pair is a poker hand which contains two cards of the same rank, 
	 * plus three other unmatched cards
	 * 
	 * "Jacks or Better" is the most common variation of video poker available. 
	 * Payoffs begin at a pair of jacks or better
	 * 
	 * @return True si se tiene Jacks o mejor.
	 * @param grupos Vector con los grupos de la mano. se obtiene del metodo <code>getGrupos()</code>
	 */
	static final boolean checkJotas(Grupo[] grupos){
		// Jacks or Better solo se aplica si solo tienes un par de cartas. si el valor es mayor que J.
		
		return(grupos.length == 1 && 
			   grupos[0].tipo == Grupo.PAR && 
			   grupos[0].carta.valor >= Carta.J);
	}
	
	/**
	 * A poker hand which contains two cards of the same rank, 
	 * plus two cards of another rank (that match each other but not the first pair), 
	 * plus one unmatched card, is called two pair

	 * 
	 * @return True si se tiene Dos pares.
	 * @param grupos Vector con los grupos de la mano. se obtiene del metodo <code>getGrupos()</code>
	 */
	static final boolean checkDobleP(Grupo[] grupos){
		return(grupos.length == 2 && 
			   grupos[0].tipo == Grupo.PAR && 
			   grupos[1].tipo == Grupo.PAR);
	}
	
	/**
	 * Three of a kind (Trio), also called trips or a set, 
	 * is a poker hand which contains three cards of the same rank, 
	 * plus two unmatched cards.
	 * 
	 * @return True si se tiene un Trio.
	 * @param grupos Vector con los grupos de la mano. se obtiene del metodo <code>getGrupos()</code>
	 */
	static final boolean checkTrio(Grupo[] grupos){
		return(grupos.length == 1 && 
			   grupos[0].tipo == Grupo.TRIO);
		
	}
	
	/**
	 * A full house, also known as a full boat, is a hand which contains three matching cards of one rank, 
	 * and two matching cards of another rank
	 * 
	 * @return True si se tiene un Full House.
	 * @param grupos Vector con los grupos de la mano. se obtiene del metodo <code>getGrupos()</code>
	 */
	//TODO: !!!
	static final boolean checkFull(Grupo[] grupos){
		return(grupos.length == 2 &&  
			  ((grupos[0].tipo == Grupo.TRIO && grupos[1].tipo == Grupo.PAR) || 
					  (grupos[0].tipo == Grupo.PAR && grupos[1].tipo == Grupo.TRIO) ));
	}
	
	/**
	 * Four of a kind, also known as quads which contains four cards of one rank, 
	 * and an unmatched card of another rank
	 * 
	 * @return True si se tiene un Quad.
	 * @param grupos Vector con los grupos de la mano. se obtiene del metodo <code>getGrupos()</code>
	 */
	static final boolean checkCuatro(Grupo[] grupos){
		return(grupos.length == 1 && 
			   grupos[0].tipo == Grupo.CUATRO);
	}
	
	
	/**
	 * Este metodo recorre el vector y "agrupa" las cartas de acuerdo a su valor. 
	 * Encuentra las cartas repetidas por valor y forma grupos.
	 * los grupos pueden ser PAR, TRIO, CUATRO
	 * No es lo mas bonito del mundo. y se puede mejorar
	 * TODO: Mejorar este metodo. 
	 * 
	 * @return Un vector con los grupos encontrados en la mano.
	 */
	public static final Grupo[] getGrupos(){
		Carta currCartas[] = {currentCartas[0], currentCartas[1], currentCartas[2], currentCartas[3], currentCartas[4]};
		Util.quicksort(currCartas);
		int repeticiones[] = {1,1,1,1,1};
		Vector repetidos = new Vector(5,1);
		
		for(int i = 0; i < 4; i++){
			if(currCartas[i].valor == currCartas[i+1].valor){
				if(!repetidos.contains(currCartas[i+1])){
					repetidos.addElement(currCartas[i+1]);
					repeticiones[repetidos.indexOf(currCartas[i+1])] += 1;
				}else{
					repeticiones[repetidos.indexOf(currCartas[i+1])] += 1;
				}
			}
		}
		
		int index = 0;
		Carta elem;
		Grupo[] grupos = new Grupo[repetidos.size()];
		
		for(int i=0; i < repetidos.size(); i++){
			elem = (Carta) repetidos.elementAt(i);
			index = repetidos.indexOf(elem);
			grupos[i] = new Grupo(elem,repeticiones[index]);
			
		}
		return grupos;
	}
	
	
}
